/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fing.edu.uy.esbadp.modules.adpMotorMonitor.wsdl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import javax.xml.namespace.QName;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.fing.edu.uy.esbadp.core.compare.wsdl.ParameterDiff;
import org.fing.edu.uy.esbadp.core.compare.wsdl.ParameterDiff.TypeValue;
import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.ow2.easywsdl.schema.api.*;
import org.ow2.easywsdl.wsdl.api.Operation;
import org.ow2.easywsdl.wsdl.api.Part;
import org.ow2.easywsdl.wsdl.api.abstractItf.AbsItfParam;

/**
 *
 * @author JORGE
 */
public class CompareDocument {
    
    private static final java.util.logging.Logger logger = LoggerADP.getLogger(CompareDocument.class.getSimpleName());
    
    public static final String partDefault = "parameters";
    
    /**
     * Compare two operations and return a list of difference parameters.
     * @param op0 Operation of wsdl0
     * @param op1 Operation of wsdl1
     * @return List<ParameterDiff> - Return a list of ParameterDiff Objects.
     */
    public static List<ParameterDiff> compareOperationInput(Operation op0, Operation op1, List<Schema> schemas0, List<Schema> schemas1) {
        List<ParameterDiff> parametersDiff = new LinkedList<ParameterDiff>();
        try{
            QName qname0 = op0.getQName();
            QName qname1 = op1.getQName();

            //TODO - cambiar despues para que de la lista de schema se obtenga el correcto namespace en caso de que haya mas de 1 import en el wsdl
            Schema schema0 = schemas0.get(0);
            Schema schema1 = schemas1.get(0);

            Type type0 = schema0.getType(qname0);
            Type type1 = schema1.getType(qname1);

            equalsComplexType((ComplexType)type0, (ComplexType)type1, parametersDiff);
        }catch(Exception ex){
            logger.log(Level.SEVERE, "There was an error in the comparison of the type wsdl Document. See StackTrace.\n[{0}]", ExceptionUtils.getStackTrace(ex));
        }
        return parametersDiff;
    }
    
    public static void equalsComplexType(ComplexType params0, ComplexType params1, List<ParameterDiff> parametersDiff) throws XmlException {  
        List<Element> unProcessedElements0 = new ArrayList<Element>(params0.getSequence().getElements());
        
        int pos = 0;
        for(Element element1 : params1.getSequence().getElements()){
            
            Iterator<Element> it = unProcessedElements0.iterator();
            boolean found = false;
            while(it.hasNext()){
                Element element0 = it.next();
                //comparo si existe un parametro con igual nombre en ambos wsdl
                if(element1.getQName().getLocalPart().equals(element0.getQName().getLocalPart())){
                    it.remove();
                    found = true;
                    break;
                }
            }
            
            //si no se encontro ningun parametro igual, entonces es un new+ o un rename de parametro
            if(!found){
                Element element0 = params0.getSequence().getElements().size() > pos ? params0.getSequence().getElements().get(pos) : null;
                //se en la misma posicion del wsdl viejo encuntro un parametro con mismo Type, asumo que es un rename
                if(element0 != null && equalsTypeParameter(element0.getType(), element1.getType()) && notExistInNewWsdl(element0.getQName().getLocalPart(), params1.getSequence().getElements())){
                    unProcessedElements0.remove(element0);
                    parametersDiff.add(new ParameterDiff(element0.getQName().getLocalPart(), element1.getQName().getLocalPart(), ParameterDiff.Modification.RENAME));
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "    Rename Parameter Name --> {0} change for {1}", new Object[]{element0.getQName().getLocalPart(), element1.getQName().getLocalPart()});
                    }
                }else{
                    //se agrega un parametro nuevo
                    ParameterDiff paramDiff = new ParameterDiff(element1.getQName().getLocalPart(), ParameterDiff.Modification.NEW);
                    paramDiff.setTypeValue(getType(element1.getType().getQName().getLocalPart()));
                    if (pos > 0) { // si no es el primer elemento tiene hermano anterior
                        paramDiff.setBrotherNameNode(params1.getSequence().getElements().get(pos - 1).getQName().getLocalPart());
                    }
                    parametersDiff.add(paramDiff);
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "    New Parameter Name --> {0}", element1.getQName().getLocalPart());
                    }
                }
            }
            
            pos++;
        }
        
        for(Element element0 : unProcessedElements0){
            //el parametro fue eliminado del nuevo wsdl
            parametersDiff.add(new ParameterDiff(element0.getQName().getLocalPart(), ParameterDiff.Modification.DELETE));
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "    Remove Parameter Name --> {0}", element0.getQName().getLocalPart());
            }
        }
    }
    
	public static boolean equalsParts(AbsItfParam params0, AbsItfParam params1, List<Schema> schemas0, List<Schema> schemas1) {
        Part parameters0 = params0.getPart(partDefault);
        Part parameters1 = params1.getPart(partDefault);
        
        QName qname0 = parameters0.getElement().getQName();
        QName qname1 = parameters1.getElement().getQName();

        //TODO - cambiar despues para que de la lista de schema se obtenga el correcto namespace en caso de que haya mas de 1 import en el wsdl
        Schema schema0 = schemas0.get(0);
        Schema schema1 = schemas1.get(0);
        
        ComplexType type0 = (ComplexType) schema0.getType(qname0);
        ComplexType type1 = (ComplexType) schema1.getType(qname1);
        
        int index = 0;
        for(Element element0 : type0.getSequence().getElements()){
            Element element1 = type1.getSequence().getElements().size() > index ? type1.getSequence().getElements().get(index) : null;
            if(element1 == null){
                return false;
            }else{
                if(!equalsTypeParameter(element0.getType(), element1.getType())){
                    return false;
                }
            }
        }
        return true;
    }
    
    public static boolean equalsTypeParameter(Type type0, Type type1) {
        return type0.equals(type1);
    }

    private static boolean notExistInNewWsdl(String localPart, List<Element> elements) {
        Iterator<Element> it = elements.iterator();
        while(it.hasNext()){
            Element aux = it.next();
            if(localPart.equals(aux.getQName().getLocalPart())){
                return false;
            }
        }
        return true;  
    }
    
    private static TypeValue getType(String localPart) {
		if(localPart.equalsIgnoreCase("int") || localPart.equalsIgnoreCase("double") || localPart.equalsIgnoreCase("float") || localPart.equalsIgnoreCase("decimal")){
			return TypeValue.NUMERIC;
		}else if(localPart.equalsIgnoreCase("string")){
			return TypeValue.STRING;
		}else{
			return TypeValue.NONE;
		}
	}
    
}
